/* Examples/jerasure_06.cpp
 * James S. Plank

Jerasure - A C/C++ Library for a Variety of Reed-Solomon and RAID-6 Erasure Coding Techniques
Copright (C) 2007 James S. Plank

James S. Plank
Department of Electrical Engineering and Computer Science
University of Tennessee 
Knoxville, TN 37996
plank@cs.utk.edu
*/

/*
 * $Revision: 1.2 $
 * $Date: 2008/08/19 17:41:40 $
 */
    
/*
	revised by S. Simmerman
	2/25/08  
*/
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jerasure-2.h"

#define talloc(type, num) (type *) malloc(sizeof(type)*(num))

void usage(char *s)
{
  fprintf(stderr, "usage: jerasure_06 k m w packetsize\n");
  fprintf(stderr, "Does a simple Cauchy Reed-Solomon coding example in GF(2^w).\n");
  fprintf(stderr, "       \n");
  fprintf(stderr, "       k+m must be < 2^w.  Packetsize must be a multiple of sizeof(long)\n");
  fprintf(stderr, "       It sets up a Cauchy distribution matrix and encodes k devices of w*packetsize bytes.\n");
  fprintf(stderr, "       After that, it decodes device 0 by using JER_Make_Decoding_Bitmatrix()\n");
  fprintf(stderr, "       and JER_Bitmatrix_Dotprod().\n");
  fprintf(stderr, "       \n");
  fprintf(stderr, "This demonstrates: JER_Bitmatrix_Encode()\n");
  fprintf(stderr, "                   JER_Bitmatrix_Decode()\n");
  fprintf(stderr, "                   JER_Bitmatrix::Print()\n");
  fprintf(stderr, "                   JER_Make_Decoding_Bitmatrix()\n");
  fprintf(stderr, "                   JER_Bitmatrix_Dotprod()\n");
  if (s != NULL) fprintf(stderr, "\n%s\n\n", s);
  exit(1);
}

static void print_data_and_coding(JER_Slices *slices)
{
	int i, j, x;
	int k, m, w, size;
	int n, sp;
	uint64_t l;

	k = slices->K;
	m = slices->M;
	w = slices->W;
	size = slices->PS;

	if(k > m) n = k;
	else n = m;
	sp = size * 2 + size/8 + 13;

	printf("%-*sCoding\n", sp, "Data");
	for (i = 0; i < n; i++) {
		for (j = 0; j< w; j++) {
			if (i < k) {
				if (j == 0) printf("D%-2d: p%-2d:", i, j);
				else        printf("     p%-2d:", j);

				for (x = 0; x < size; x+=8) {
					memcpy(&l, slices->ptrs[i]+j*size+x, sizeof(uint64_t));
					printf(" %016lx", l);
				}
				printf("    ");
			}
			else printf("%*s", sp, "");
			if (i < m) {
				if (j == 0) printf("C%-2d: p%-2d:", i, j);
				else        printf("     p%-2d:", j);
				for ( x = 0; x < size; x+=8) {
					memcpy(&l, slices->ptrs[k+i] + j*size + x, sizeof(uint64_t));
					printf(" %016lx", l);
				}
			}
			printf("\n");
		}
	}
	printf("\n");
}

int main(int argc, char **argv)
{
  uint64_t l;
  int k, w, i, j, m, psize, x, sou;
	JER_Matrix *jm;
	JER_Bitmatrix *jbm;
	JER_Slices *slices;
	vector <int> erasures, erased;
	JER_Bitmatrix *dm;
	int *dm_ids;
	unsigned char *data;

  if (argc != 5) usage(NULL);
  if (sscanf(argv[1], "%d", &k) == 0 || k <= 0) usage((char *)"Bad k");
  if (sscanf(argv[2], "%d", &m) == 0 || m <= 0) usage((char *)"Bad m");
  if (sscanf(argv[3], "%d", &w) == 0 || w <= 0 || w > 32) usage((char *)"Bad w");
  if (w < 30 && (k+m) > (1 << w)) usage((char *)"k + m is too big");
  if (sscanf(argv[4], "%d", &psize) == 0 || psize <= 0) usage((char *)"Bad packetsize");
  if(psize%sizeof(long) != 0) usage((char *)"Packetsize must be multiple of sizeof(long)");

  jm = new JER_Matrix(m, k, w);

  for (i = 0; i < m; i++) {
    for (j = 0; j < k; j++) {
			jm->Elts[i][j] = galois_single_divide(1, i ^ (m + j), w);
    }
  }

  jbm = new JER_Bitmatrix(*jm);

  printf("Last (m * w) rows of the Binary Distribution Matrix:\n\n");
  printf("\n");

  srand48(0);
	 slices = new (nothrow) JER_Slices();
	slices->K = k;
	slices->M = m;
	slices->W = w;
	slices->PS = psize;
	slices->size = psize*w;

	sou = sizeof(uint64_t);

	slices->ptrs.resize(k + m);

	data = new unsigned char [k * w * m * w * psize];

  for (i = 0; i < k; i++) {
		slices->ptrs[i] = data + i*w*psize;
    for (j = 0; j < w; j++) {
			for (x = 0; x < psize; x += sou) {
				l = lrand48();
				l <<= 8*4;
				l += lrand48();
				memcpy(data + i*psize*w + j*psize + x, &l, sou);
			}
		}
	}

	for (i = 0; i < m; i++) {
		slices->ptrs[k + i] = data + k*psize*w + i*w*psize;
	}

	JER_Bitmatrix_Encode(slices, jbm);
  
  printf("Encoding Complete:\n\n");
  print_data_and_coding(slices);

	erasures.resize(m);
	erased.resize(k+m);

  for (i = 0; i < m+k; i++) erased[i] = 0;
  for (i = 0; i < m; ) {
    erasures[i] = lrand48()%(k+m);
    if (erased[erasures[i]] == 0) {
      erased[erasures[i]] = 1;
      bzero(slices->ptrs[erasures[i]], psize*w);
      i++;
    }
  }
	cout << endl;

  printf("Erased %d random devices:\n\n", m);
  print_data_and_coding(slices);
  
	i = JER_Bitmatrix_Decode(slices, jbm, 0, erasures);

	printf("Retval of Decode: %d\n", i);
  printf("State of the system after decoding:\n\n");
  print_data_and_coding(slices);
  
	dm = new JER_Bitmatrix(k, k, w);
	dm_ids = new int [k];

  for (i = 0; i < m; i++) erased[i] = 1;
  for (; i < k+m; i++) erased[i] = 0;

	JER_Make_Decoding_Bitmatrix(jbm, erased, dm, dm_ids);

  printf("Suppose we erase the first %d devices.  Here is the decoding matrix:\n\n", m);
	dm->Print();
  printf("\n");
	printf("And dm_ids:\n\n");
	for (i = 0; i < k; i++) {
		printf(" ");
		if (i % w == 0 && i > 0) printf(" ");
		printf("%d", dm_ids[i]);
	}

  bzero(data, w*psize);
	JER_Bitmatrix_Dotprod(slices, dm, 0, dm_ids, 0);

  printf("\nAfter calling jerasure_matrix_dotprod, we calculate the value of device #0, packet 0 to be:\n");
	printf("\nD0  p0 :");
	for(i = 0; i < psize; i +=sou) {
		memcpy(&l, data+i, sou);
		printf(" %016lx", l);
	}
	printf("\n\n");
  //memcpy(&l, data[0], sizeof(long));

  return 0;
}
